#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>

#include "cJSON.h"
#include "clog_lib.h"
#include "clog_log.h"
#include "clog_common.h"
#include "clog_json_utils.h"

static cJSON *g_clog_json_root_info = NULL;

/**
 * @brief Parse root information from clog configuration file.
 *
 * This function parses the root JSON structure from the specified clog configuration file.
 * It performs file validation (existence, size), reads the file content into memory,
 * and initializes the global JSON root pointer for subsequent configuration parsing.
 * Error handling is included for file operations, memory allocation, and JSON parsing failures.
 *
 * @param clog_cfg_file Path to the clog configuration JSON file (must not be NULL).
 * @return int32_t RET_OK (0) on success; negative error code on failure:
 *         - -RET_ERR: Invalid file size (zero or stat failed)
 *         - -ERR_CODE_OPEN_FILE: Failed to open the configuration file
 *         - -ERR_CODE_MALLOC: Memory allocation for file buffer failed
 *         - -ERR_CODE_PARSE_JSON_FAIL: JSON parsing of file content failed
 */
static int32_t clog_cfg_parse_root_info(const char *clog_cfg_file)
{
    int32_t ret = RET_OK;
    uint32_t read_size;
    char *filebuf = NULL;
    FILE *clog_cfg_fp = NULL;
    struct stat statbuf;

    /* add for support more interface input s */
    if(clog_cfg_file == NULL) {
        clog_log_error("clog_cfg_file is null !!\n");
        return -RET_ERR;
    }

    clog_log_info("clog_cfg_file name = %s\n", clog_cfg_file);

    /* get file size */
    ret = stat(clog_cfg_file, &statbuf);
    if((ret < 0) || (0 == statbuf.st_size)) {
        clog_log_error("clog config file size is zero !!\n");
        return -RET_ERR;
    }

    clog_cfg_fp = fopen(clog_cfg_file,"r");
    if(clog_cfg_fp == NULL) {
        clog_log_error("open %s fail!!\n", clog_cfg_file);
        return -ERR_CODE_OPEN_FILE;
    }

    filebuf = (char *)malloc((size_t)statbuf.st_size + 1UL);
    if(NULL == filebuf) {
        clog_log_error("malloc buff fail!!\n");
        (void)fclose(clog_cfg_fp);
        return -ERR_CODE_MALLOC;
    }

    (void)memset((void *)filebuf, 0, (size_t)(statbuf.st_size) + 1u);
    read_size = (uint32_t)fread((void *)filebuf, (size_t)statbuf.st_size, (size_t)1, clog_cfg_fp);
    if (read_size < 1u) {
        clog_log_error("read json fail\n");
        (void)fclose(clog_cfg_fp);
        free((void *)filebuf);
        return -RET_ERR;
    }

    g_clog_json_root_info = cJSON_Parse((const char *)filebuf);
    if(NULL == g_clog_json_root_info) {
        clog_log_error("parse json root info fail\n");
        (void)fclose(clog_cfg_fp);
        free((void *)filebuf);
        return -ERR_CODE_PARSE_JSON_FAIL;
    }

    (void)fclose(clog_cfg_fp);
    free((void *)filebuf);

    return ret;
}

/**
 * @brief Parse global configuration from JSON root.
 *
 * This function parses global configuration parameters from the JSON root structure.
 * It extracts debug level, log level, log directory, log file, ring buffer size,
 * asynchronous buffer size, and asynchronous flush interval. Error handling is included
 * for JSON parsing failures and invalid parameter values.
 *
 * @param global_info Pointer to the global_info_t structure to store parsed configuration.
 * @return int32_t RET_OK (0) on success; negative error code on failure:
 *         - -RET_ERR: Global JSON root info is null
 *         - -ERR_CODE_PARSE_JSON_FAIL: JSON parsing of global info failed
 */
int32_t clog_cfg_parse_global_info(global_info_t *global_info)
{
    cJSON *tmp_node = NULL;
    cJSON *clog_json_global_info = NULL;

    if(NULL == g_clog_json_root_info) {
        clog_log_error("g_clog_json_root_info is null\n");
        return -RET_ERR;
    }

    clog_json_global_info = cJSON_GetObjectItem(g_clog_json_root_info, "global");
    if(NULL == clog_json_global_info) {
        clog_log_error("clog_json_global_info is null\n");
        return -ERR_CODE_PARSE_JSON_FAIL;
    }

    tmp_node = cJSON_GetObjectItem(clog_json_global_info, "debug_level");
    if(NULL != tmp_node) {
        global_info->debug_level = (uint32_t)tmp_node->valueint;
        clog_log_info("debug_level = %d\n", global_info->debug_level);
    }

    tmp_node = cJSON_GetObjectItem(clog_json_global_info, "log_level");
    if(NULL != tmp_node) {
        global_info->log_level = tmp_node->valuestring;
        clog_log_info("log_level = %s\n", global_info->log_level);
    }

    tmp_node = cJSON_GetObjectItem(clog_json_global_info, "log_dir");
    if(NULL != tmp_node) {
        global_info->log_dir = tmp_node->valuestring;
        clog_log_info("log_dir = %s\n", global_info->log_dir);
    }

    tmp_node = cJSON_GetObjectItem(clog_json_global_info, "log_file");
    if(NULL != tmp_node) {
        global_info->log_file = tmp_node->valuestring;
        clog_log_info("log_file = %s\n", global_info->log_file);
    }

    tmp_node = cJSON_GetObjectItem(clog_json_global_info, "log_file_size");
    if(NULL != tmp_node) {
        global_info->log_file_size = (uint32_t)tmp_node->valueint;
        clog_log_info("log_file_size = %d\n", global_info->log_file_size);
    }

    tmp_node = cJSON_GetObjectItem(clog_json_global_info, "ring_bufs_size");
    if(NULL != tmp_node) {
        global_info->ring_bufs_size = (uint32_t)tmp_node->valueint;
        clog_log_info("ring_bufs_size = %d\n", global_info->ring_bufs_size);
    }

    tmp_node = cJSON_GetObjectItem(clog_json_global_info, "async_buf_size");
    if(NULL != tmp_node) {
        global_info->async_buf_size = (uint32_t)tmp_node->valueint;
        clog_log_info("async_buf_size = %d\n", global_info->async_buf_size);
    }

    tmp_node = cJSON_GetObjectItem(clog_json_global_info, "async_interval");
    if(NULL != tmp_node) {
        global_info->async_interval = (uint32_t)tmp_node->valueint;
        clog_log_info("async_interval = %d\n", global_info->async_interval);
    }

    tmp_node = cJSON_GetObjectItem(clog_json_global_info, "shm_size");
    if(NULL != tmp_node) {
        global_info->shm_size = (uint32_t)tmp_node->valueint;
        clog_log_info("shm_size = %d\n", global_info->shm_size);
    }

    return RET_OK;
}

/**
 * @brief Parse module configuration from JSON root.
 *
 * This function parses module configuration parameters from the JSON root structure.
 * It extracts module name, log file, module ID, module enable, and log level.
 * Error handling is included for JSON parsing failures and invalid parameter values.
 *
 * @param module_info_list Pointer to the module_info_list_t structure to store parsed configuration.
 * @return int32_t RET_OK (0) on success; negative error code on failure:
 *         - -RET_ERR: Global JSON root info is null
 *         - -ERR_CODE_PARSE_JSON_FAIL: JSON parsing of module info failed
 */
int32_t clog_cfg_parse_module_info_list(module_info_list_t *module_info_list)
{
    int32_t ret = RET_OK;
    uint32_t array_index = 0;
    cJSON *tmp_node = NULL;
    cJSON *tmp_module_node = NULL;
    cJSON *clog_json_module_info = NULL;
    char tmp_node_name[CLOG_PARSE_LENGTH] = {0};

    if(NULL == g_clog_json_root_info) {
        clog_log_error("g_clog_json_root_info is null\n");
        return -RET_ERR;
    }

    clog_json_module_info = cJSON_GetObjectItem(g_clog_json_root_info, "modules");
    if(NULL != clog_json_module_info) {
        tmp_node = cJSON_GetObjectItem(clog_json_module_info, "module_num");
        if(NULL != tmp_node) {
            module_info_list->module_num = (uint32_t)tmp_node->valueint;
            clog_log_info("module_num = %d\n", module_info_list->module_num);
        } else {
            clog_log_error("module_num is null\n");
            return -ERR_CODE_PARSE_JSON_FAIL;
        }

        for(array_index = 0; array_index < module_info_list->module_num; array_index++) {
            (void)snprintf(tmp_node_name, sizeof(tmp_node_name), "module_%d", array_index);
            tmp_module_node = cJSON_GetObjectItem(clog_json_module_info, (const char *)tmp_node_name);
            if(NULL != tmp_module_node) {
                module_info_list->module_info[array_index].module_id = array_index;
                tmp_node = cJSON_GetObjectItem(tmp_module_node, "module_name");
                if(NULL != tmp_node) {
                    module_info_list->module_info[array_index].module_name = tmp_node->valuestring;
                    clog_log_info("module_name = %s\n", module_info_list->module_info[array_index].module_name);
                }

                tmp_node = cJSON_GetObjectItem(tmp_module_node, "module_id");
                if(NULL != tmp_node) {
                    module_info_list->module_info[array_index].module_id = (uint32_t)tmp_node->valueint;
                    clog_log_info("module_id = %d\n", module_info_list->module_info[array_index].module_id);
                }

                tmp_node = cJSON_GetObjectItem(tmp_module_node, "module_enable");
                if(NULL != tmp_node) {
                    module_info_list->module_info[array_index].module_enable = (uint32_t)tmp_node->valueint;
                    clog_log_info("module_enable = %d\n", module_info_list->module_info[array_index].module_enable);
                }

                tmp_node = cJSON_GetObjectItem(tmp_module_node, "log_level");
                if(NULL != tmp_node) {
                    module_info_list->module_info[array_index].log_level = tmp_node->valuestring;
                    clog_log_info("log_level = %s\n", module_info_list->module_info[array_index].log_level);
                }

                tmp_node = cJSON_GetObjectItem(tmp_module_node, "shm_key");
                if(NULL != tmp_node) {
                    module_info_list->module_info[array_index].shm_key = tmp_node->valuestring;
                    clog_log_info("shm_key = %s\n", module_info_list->module_info[array_index].shm_key);
                }

                tmp_node = cJSON_GetObjectItem(tmp_module_node, "log_file");
                if(NULL != tmp_node) {
                    module_info_list->module_info[array_index].log_file = tmp_node->valuestring;
                    clog_log_info("log_file = %s\n", module_info_list->module_info[array_index].log_file);
                }
            }
        }
    }

    return ret;
}

/**
 * @brief Parse JSON configuration file.
 *
 * This function parses a JSON configuration file specified by the provided file path.
 * It populates the provided clog_config_t structure with parsed configuration parameters.
 * Error handling is included for file opening, JSON parsing, and invalid parameter values.
 *
 * @param clog_cfg_file Path to the JSON configuration file.
 * @param clog_cfg Pointer to the clog_config_t structure to store parsed configuration.
 * @return int32_t RET_OK (0) on success; negative error code on failure:
 *         - -RET_ERR: Global JSON root info is null
 *         - -ERR_CODE_PARSE_JSON_FAIL: JSON parsing of module info failed
 */
int32_t clog_json_parse_file(const char *clog_cfg_file, clog_config_t *clog_cfg)
{
    int32_t ret = RET_OK;
    
    /* parse root config */
    ret = clog_cfg_parse_root_info(clog_cfg_file);
    if(ret < 0) {
        clog_log_error("parse json fail, ret = %d\n", ret);
        return ret;
    }

    /* parse global config */
    ret = clog_cfg_parse_global_info(&clog_cfg->global_info);
    if(ret < 0) {
        clog_log_error("parse json fail, ret = %d\n", ret);
        return ret;
    }

    /* parse module config */
    ret = clog_cfg_parse_module_info_list(&clog_cfg->module_info_list);
    if(ret < 0) {
        clog_log_error("parse json fail, ret = %d\n", ret);
        return ret;
    }

    return ret;
}

/**
 * @brief Free JSON root node.
 *
 * This function frees the memory allocated for the JSON root node.
 *
 * @param root Pointer to the JSON root node to be freed.
 */
int32_t clog_json_free(void)
{
    if (g_clog_json_root_info) {
        cJSON_Delete(g_clog_json_root_info);
        g_clog_json_root_info = NULL;
        return RET_OK;
    }
    return RET_ERR;
}